import { Command, InvalidOptionArgumentError } from 'commander';
import chalk from 'chalk';
import inquirer from 'inquirer';
import readline from 'readline';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { spawnSync } from 'child_process';
import { createLogger } from '../utils/Logger.js';
import { getGlobalOpsFile } from '../utils/OpsFile.js';
import { fetchUserOpsToken } from '../utils/OpsUser.js';
import { opsClient } from '../utils/OpsClient.js';
import process from 'process';
import boxen from 'boxen';

const ENV_MAPPING = {
    f: {
        namespace: 'hspt-feature',
        cluster: 'nacos-gt-config-szgds.hszq8.com',
    },
    u: {
        namespace: 'hspt-daily',
        cluster: 'nacos-gt-config-szgds.hszq8.com',
    },
    d: {
        namespace: 'hspt-daily',
        cluster: 'nacos-gt-config-szgds.hszq8.com',
    },
    b: {
        namespace: 'beta',
        cluster: 'nacos-vip-config.hszq8.com',
    },
    p: {
        namespace: 'product',
        cluster: 'nacos-vip-config.hszq8.com',
    },
};

class NacosConfigCommandHandler {
    constructor() {
        this.program = new Command();
        this.logger = createLogger('NACOS');
        this.globalOpsFile = getGlobalOpsFile();
        this.setupCommands();
    }

    setupCommands() {
        this.program
            .name('nacos-config')
            .description('查询 Nacos 配置文件内容并输出到控制台，需显式指定环境')
            .argument('<fileName>', '配置文件 dataId')
            .option('-f, --feature', 'feature 环境', false)
            .option('-u, --uat', 'uat/daily 环境', false)
            .option('-d, --daily', 'daily 环境（与 -u 等价）', false)
            .option('-b, --beta', 'beta 环境', false)
            .option('-p, --prod', 'prod 环境', false)
            .option('--env <env>', '备用：直接传 f/u/d/b/p')
            .option('--verbose', '打印调试信息', false)
            .action((fileName, options) => {
                this.handleCommand(fileName, options).catch((error) => {
                    this.logger.error(error.message);
                    process.exitCode = 1;
                });
            });

        this.program.addHelpText('after', () => `
示例：
  nacos-config -u hs-ipo-base-env.properties     查询 UAT/Daily 配置
  nacos-config -f hs-ipo-base-env.properties     查询 Feature 配置
  nacos-config --env b hs-ipo-base-env.properties 查询 Beta 配置

说明：
  - 必须显式指定环境：-f/-u/-d/-b/-p 或 --env f|u|d|b|p
  - 仅取第一页 10 条结果，如有多条会提示选择
`);
    }

    async handleCommand(fileName, options) {
        const verbose = Boolean(options.verbose);
        const positionalFileName = (fileName || '').trim();

        const envCode = this.resolveEnvFromOptions(options);
        const finalFileName = positionalFileName;

        this.logger.debug?.(`接收到 fileName=${finalFileName}, env=${envCode}`);

        this.validateInput(envCode, finalFileName);

        const envMeta = ENV_MAPPING[envCode];
        const opsUser = this.globalOpsFile.read('ops_user');
        if (!opsUser) {
            throw new Error('未找到 ops_user，全局配置缺失，请先运行 nb-init');
        }

        const token = await fetchUserOpsToken(opsUser);
        this.opsClient = opsClient(token, verbose);

        const searchResult = await this.opsClient.searchNacosConfig(
            envMeta.cluster,
            envMeta.namespace,
            finalFileName,
            10,
            1
        );
        const pageItems = searchResult?.data?.pageItems || [];

        if (!Array.isArray(pageItems) || pageItems.length === 0) {
            console.log(chalk.yellow('暂未找到配置文件'));
            return;
        }

        const targetItem = await this.pickConfig(pageItems);

        const dataId = targetItem.dataId || targetItem.data_id || finalFileName;
        const group = targetItem.group || '';

        const contentResult = await this.opsClient.getNacosConfiguration(
            envMeta.cluster,
            envMeta.namespace,
            dataId,
            group
        );
        const configData = contentResult?.data;
        if (!configData) {
            throw new Error('未获取到配置内容，请稍后重试');
        }

        this.renderConfig(configData);
        await this.runViFlow(envMeta, configData, dataId, group, opsUser);
    }

    validateInput(envCode, fileName) {
        if (!envCode || !ENV_MAPPING[envCode]) {
            throw new Error('环境参数错误，请使用 -f/-u/-d/-b/-p 或 --env f|u|d|b|p 指定环境');
        }
        if (!fileName) {
            throw new Error('文件名不能为空');
        }
    }

    resolveEnvFromOptions(options) {
        const candidates = [];
        if (options.feature) candidates.push('f');
        if (options.uat) candidates.push('u');
        if (options.daily) candidates.push('d');
        if (options.beta) candidates.push('b');
        if (options.prod) candidates.push('p');

        const envParam = (options.env || '').trim().toLowerCase();
        if (envParam) {
            candidates.push(envParam);
        }

        const unique = Array.from(new Set(candidates));
        if (unique.length === 0) {
            throw new InvalidOptionArgumentError('请通过 -f/-u/-d/-b/-p 或 --env f|u|d|b|p 指定环境');
        }
        if (unique.length > 1) {
            throw new InvalidOptionArgumentError(`检测到多个环境参数：${unique.join(', ')}，请仅保留一个`);
        }

        const envCode = unique[0];
        return envCode;
    }

    async pickConfig(items) {
        if (items.length === 1) {
            return items[0];
        }

        const choices = items.map((item, index) => {
            const dataId = item.dataId || item.data_id || '-';
            const group = item.group || '-';
            const appName = item.appName || '-';
            return {
                name: `${index + 1}. ${dataId} | ${group} | ${appName}`,
                value: item,
            };
        });

        const answers = await inquirer.prompt([
            {
                type: 'list',
                name: 'selectedItem',
                message: '找到多个配置文件，请选择：',
                choices,
            },
        ]);
        return answers.selectedItem;
    }

    renderConfig(config) {
        const dataId = config.dataId || config.data_id || '-';
        const group = config.group || '-';
        const appName = config.appName || '-';
        const modifyTime = config.modifyTime || '-';
        const content = config.content || '';

        console.log(chalk.green('=== 配置元信息 ==='));
        console.log(`dataId     : ${dataId}`);
        console.log(`group      : ${group}`);
        console.log(`appName    : ${appName}`);
        console.log(`modifyTime : ${modifyTime}`);
        console.log('\n--- 配置内容（便于复制粘贴） ---');
        console.log(content || '');
    }

    async runViFlow(envMeta, configData, dataId, group, opsUser) {
        const originalContent = configData.content || '';
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });

        const ask = (q) => new Promise((resolve) => rl.question(q, resolve));

        console.log(chalk.cyan('\n=== 进入 VI 交互模式 ==='));
        console.log('是否需要修改文件？如需修改请回车，不需要请按 Ctrl+C 退出');
        console.log('提示：进入 vi 后按 [i] 开始覆盖编辑，按 [Esc] 后输入 q! 放弃，:wq 保存退出');

        const enter = await ask('> ');
        const trimmed = (enter || '').trim();
        if (trimmed === ':q!') {
            rl.close();
            console.log(chalk.yellow('已退出 vi 模式，未做任何修改'));
            return;
        }

        rl.close();
        const newContent = this.openInEditor(originalContent);
        if (newContent === originalContent) {
            console.log(chalk.yellow('内容未变化，取消提交'));
            return;
        }

        this.printDiff(originalContent, newContent);

        const answers = await inquirer.prompt([
            {
                type: 'input',
                name: 'reason',
                message: '请输入变更说明（同时用于 update_reason / update_affect）：',
                validate: (input) => {
                    if (!input || !input.trim()) {
                        return '变更说明不能为空';
                    }
                    return true;
                },
            },
        ]);
        const reason = answers.reason.trim();

        const submitBody = this.buildSubmitBody(envMeta, configData, dataId, group, newContent, opsUser, reason);
        const submitResult = await this.opsClient.submitNacosConfigChange(submitBody);
        const approvalId = submitResult?.data?.approval_id;
        if (!approvalId) {
            throw new Error('未获取到 approval_id，提交失败');
        }
        console.log(chalk.green(`已提交变更，审批单号：${approvalId}`));

        const pollResult = await this.pollApprovalAndPublish(envMeta, approvalId);
        if (!pollResult.published) {
            console.log(chalk.yellow(`审批状态未满足发布条件，当前：approval=${pollResult.approvalStatus} op=${pollResult.opStatus}`));
            return;
        }

        console.log(chalk.green('发布成功，按回车刷新查看最新内容，或输入 q 退出'));
        const rl2 = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        const cmd = await new Promise((resolve) => rl2.question('', resolve));
        rl2.close();
        if ((cmd || '').trim().toLowerCase() === 'q') {
            console.log(chalk.yellow('已退出'));
            return;
        }

        const latest = await this.opsClient.getNacosConfiguration(
            envMeta.cluster,
            envMeta.namespace,
            dataId,
            group
        );
        const latestData = latest?.data;
        if (!latestData) {
            console.log(chalk.red('刷新最新内容失败'));
            return;
        }
        console.log(chalk.cyan('\n=== 最新配置内容 ==='));
        this.renderConfig(latestData);
    }

    printDiff(oldText, newText) {
        const oldLines = (oldText || '').split('\n');
        const newLines = (newText || '').split('\n');
        const maxLen = Math.max(oldLines.length, newLines.length);
        const diffLines = [];
        for (let i = 0; i < maxLen; i += 1) {
            const oldLine = oldLines[i];
            const newLine = newLines[i];
            if (oldLine === newLine) {
                diffLines.push(`  ${oldLine ?? ''}`);
                continue;
            }
            if (oldLine !== undefined) {
                diffLines.push(chalk.red(`- ${oldLine}`));
            }
            if (newLine !== undefined) {
                diffLines.push(chalk.green(`+ ${newLine}`));
            }
        }
        console.log(chalk.cyan('\n=== 变更 Diff（红色删除，绿色新增） ==='));
        console.log(diffLines.join('\n'));
    }

    openInEditor(content) {
        const editor = process.env.EDITOR || 'vi';
        const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nacos-config-'));
        const tmpFile = path.join(tmpDir, 'config.txt');
        fs.writeFileSync(tmpFile, content || '', 'utf-8');

        console.log(chalk.gray(`打开编辑器: ${editor} ${tmpFile}`));
        const result = spawnSync(editor, [tmpFile], {
            stdio: 'inherit',
        });
        if (result.error) {
            console.log(chalk.red(`调用编辑器失败: ${result.error.message}`));
            return null;
        }

        let newContent = null;
        try {
            newContent = fs.readFileSync(tmpFile, 'utf-8');
        } catch (e) {
            console.log(chalk.red(`读取修改内容失败: ${e.message}`));
            return null;
        } finally {
            try {
                fs.unlinkSync(tmpFile);
                fs.rmdirSync(tmpDir);
            } catch {
                // ignore cleanup errors
            }
        }
        return newContent;
    }

    buildSubmitBody(envMeta, configData, dataId, group, content, opsUser, reason) {
        const configEnv = this.resolveConfigEnv(envMeta);
        return {
            originator_user: opsUser,
            cluster_address: envMeta.cluster,
            op_type: 'update',
            update_reason: reason,
            update_affect: reason,
            update_ns: envMeta.namespace,
            update_dataId: dataId,
            update_group: group || '',
            appName: configData.appName || '',
            config_tags: configData.configTags || configData.config_tags || '',
            config_description: configData.desc || configData.config_description || '',
            config_format: configData.type || configData.config_format || 'properties',
            config_env: configData.config_env || configEnv,
            update_content: content,
            feishu_project: '',
        };
    }

    resolveConfigEnv(envMeta) {
        const ns = envMeta.namespace || '';
        if (ns.includes('feature')) return 'feature';
        if (ns.includes('daily')) return 'daily';
        if (ns.includes('beta')) return 'beta';
        if (ns.includes('product') || ns.includes('prod')) return 'prod';
        return '';
    }

    async pollApprovalAndPublish(envMeta, approvalId) {
        const intervalMs = 2000;
        const timeoutMs = 2 * 60 * 60 * 1000;
        const start = Date.now();
        let found = null;
        while (Date.now() - start < timeoutMs) {
            const listResult = await this.opsClient.listNacosApprovals(envMeta.cluster, true);
            const items = Array.isArray(listResult?.data) ? listResult.data : [];
            found = items.find((item) => item.approval_id === approvalId);
            if (found) {
                const approvalStatus = found.approval_status || '';
                const opStatus = found.op_status || '';
                console.log(chalk.gray(`轮询中... approval=${approvalStatus} op=${opStatus}`));
                if (approvalStatus === 'agree' && opStatus === 'pending') {
                    const publishResult = await this.opsClient.publishNacosConfiguration(approvalId);
                    if (publishResult?.status === 0) {
                        return { published: true, approvalStatus, opStatus };
                    }
                    return { published: false, approvalStatus, opStatus };
                }
                if (approvalStatus === 'reject' || opStatus === 'reject' || opStatus === 'fail') {
                    return { published: false, approvalStatus, opStatus };
                }
            } else {
                console.log(chalk.gray('未找到审批记录，继续轮询...'));
            }
            await new Promise((resolve) => setTimeout(resolve, intervalMs));
        }
        const approvalStatus = found?.approval_status || 'timeout';
        const opStatus = found?.op_status || 'timeout';
        console.log(chalk.yellow('轮询超时，未自动发布'));
        return { published: false, approvalStatus, opStatus };
    }
}

export async function execute(args = []) {
    const handler = new NacosConfigCommandHandler();
    handler.program.parse(args, { from: 'user' });
}
